﻿
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace LinkingCloud.API.Common.Nets
{
    public class HttpExecuter : IHttpExecuter
    {
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly ILogger<HttpExecuter> _logger;

        public HttpExecuter(IHttpClientFactory httpClientFactory, ILogger<HttpExecuter> logger)
        {
            _httpClientFactory = httpClientFactory;
            _logger = logger;
        }

        public static List<string> DefaultHeaders = new List<string> {
            "Accept","Accept-Encoding","Connection","KeepAlive","Content-Length","Content-Type","Expect",
            "Date","Host","IfModifiedSince","Referer","User-Agent","Transfer-Encoding"
        };

        public async Task<string> GetAsync(string url, Dictionary<string, string> headers = null)
        {
            var result = string.Empty;
            var headersStr = string.Empty;
            try
            {
                headersStr = JsonConvert.SerializeObject(headers);

                _logger.LogInformation($"{url}-Post-Begin,headers:{ headersStr}");

                var client = _httpClientFactory.CreateClient();

                if (headers != null)
                {
                    foreach (var header in headers)
                    {
                        if (DefaultHeaders.Contains(header.Key))
                        {
                            continue;
                        }
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                    }
                }
                client.DefaultRequestHeaders.Add("AcceptCharset", "utf-8");
                client.DefaultRequestHeaders.Add("Accept", "*/*");


                var response = await client.GetAsync(url);

                result = await response.Content.ReadAsStringAsync();
                _logger.LogInformation($"{url}-Post-End,headers:{ headersStr},result:{result}");

                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError($"{url}-Error,headers:{ headersStr},result:{result},ErrorMsg:{ex.Message}", ex);
                throw ex;
            }
        }

        public async Task<string> PostAsync(string url, string parameters, Dictionary<string, string> headers = null)
        {
            var headersStr = string.Empty;
            var result = string.Empty;
            try
            {
                headersStr = JsonConvert.SerializeObject(headers);

                _logger.LogInformation($"{url}-Post-Begin,headers:{headersStr}");

                var client = _httpClientFactory.CreateClient();
                string contentType = "application/json";
                if (headers != null)
                {
                    foreach (var header in headers)
                    {
                        if (DefaultHeaders.Contains(header.Key))
                        {
                            if (header.Key == "Content-Type")
                            {
                                contentType = header.Value;
                            }
                            continue;
                        }
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                    }
                }

                client.DefaultRequestHeaders.Add("AcceptCharset", "utf-8");
                client.DefaultRequestHeaders.Add("Accept", "*/*");
                using (var content = new StringContent(parameters, Encoding.UTF8))
                {
                    content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);

                    var response = await client.PostAsync(url, content);

                    result = await response.Content.ReadAsStringAsync();
                    _logger.LogInformation($"{url}-Post-End,headers:{ headersStr},result:{result}");

                    return result;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"{url}-Post-End,parameters:{parameters},headers:{ headersStr},result:{result},ErrorMsg:{ex.Message}", ex);
                throw ex;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <param name="parameters"></param>
        /// <param name="headers"></param>
        /// <returns></returns>
        public string Post(string url, string parameters, Dictionary<string, string> headers = null)
        {
            var headersStr = string.Empty;
            var result = string.Empty;
            try
            {
                headersStr = JsonConvert.SerializeObject(headers);

                _logger.LogInformation($"{url}-Post-Begin,headers:{headersStr}");

                var client = _httpClientFactory.CreateClient();
                string contentType = "application/json";
                if (headers != null)
                {
                    foreach (var header in headers)
                    {
                        if (DefaultHeaders.Contains(header.Key))
                        {
                            if (header.Key == "Content-Type")
                            {
                                contentType = header.Value;
                            }
                            continue;
                        }
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                    }
                }

                client.DefaultRequestHeaders.Add("AcceptCharset", "utf-8");
                client.DefaultRequestHeaders.Add("Accept", "*/*");
                using (var content = new StringContent(parameters, Encoding.UTF8))
                {
                    content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);

                    var response = client.PostAsync(url, content).Result;

                    result = response.Content.ReadAsStringAsync().Result;
                    _logger.LogInformation($"{url}-Post-End,headers:{ headersStr},result:{result}");

                    return result;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"{url}-Post-End,parameters:{parameters},headers:{ headersStr},result:{result},ErrorMsg:{ex.Message}", ex);
                throw ex;
            }
        }

        public async Task<string> PutAsync(string url, string parameters, Dictionary<string, string> headers = null)
        {
            var client = _httpClientFactory.CreateClient();
            string contentType = "application/json";
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    if (DefaultHeaders.Contains(header.Key))
                    {
                        if (header.Key == "Content-Type")
                        {
                            contentType = header.Value;
                        }
                        continue;
                    }
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }

            using (var content = new StringContent(parameters, Encoding.UTF8))
            {
                content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);

                var response = await client.PutAsync(url, content);

                var result = await response.Content.ReadAsStringAsync();
                return result;
            }
        }

        public async Task<string> PatchAsync(string url, string parameters, Dictionary<string, string> headers = null)
        {
            var client = _httpClientFactory.CreateClient();
            string contentType = "application/json";
            if (headers != null)
            {
                foreach (var header in headers)
                {
                    if (DefaultHeaders.Contains(header.Key))
                    {
                        if (header.Key == "Content-Type")
                        {
                            contentType = header.Value;
                        }
                        continue;
                    }
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }

            using (var content = new StringContent(parameters, Encoding.UTF8))
            {
                content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);

                var response = await client.PatchAsync(url, content);

                var result = await response.Content.ReadAsStringAsync();
                return result;
            }
        }

        public async Task<string> DeleteAsync(string url, string parameters, Dictionary<string, string> headers = null)
        {
            var client = _httpClientFactory.CreateClient();

            if (headers != null)
            {
                foreach (var header in headers)
                {
                    if (DefaultHeaders.Contains(header.Key))
                    {
                        continue;
                    }
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }

            using (var content = new StringContent(parameters, Encoding.UTF8))
            {
                content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

                var response = await client.PutAsync(url, content);

                var result = await response.Content.ReadAsStringAsync();
                return result;
            }
        }


        public async Task<string> SendAsync(string url, string parameters, string method, Dictionary<string, string> headers = null)
        {
            var client = _httpClientFactory.CreateClient();

            if (headers != null)
            {
                foreach (var header in headers)
                {
                    if (DefaultHeaders.Contains(header.Key))
                    {
                        continue;
                    }
                    client.DefaultRequestHeaders.Add(header.Key, header.Value);
                }
            }

            using (var content = new StringContent(parameters, Encoding.UTF8))
            {
                content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

                var response = await client.SendAsync(new HttpRequestMessage
                {
                    Method = new HttpMethod(method),
                    Content = content,
                    RequestUri = new Uri(url)
                });

                var result = await response.Content.ReadAsStringAsync();
                return result;
            }
        }


        public async Task<string> PostAsync(string url, string parameters, string date, Dictionary<string, string> headers = null)
        {
            byte[] byteArray = Encoding.UTF8.GetBytes(parameters); //转化
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "POST";
            request.Proxy = null;
            request.ContentType = "application/json";
            request.ContentLength = byteArray.Length;
            //request.Accept = "application/json";
            request.Date = Convert.ToDateTime(date);
            //if (headers.ContainsKey("Authorization")) {
            //    request.Headers.Add(HttpRequestHeader.Authorization, headers.GetValueOrDefault("Authorization"));
            //}
            SetHeaders(request, headers);
            using (Stream reqStream = request.GetRequestStream())
            {
                reqStream.Write(byteArray, 0, byteArray.Length);//写入参数
            }
            using (var response = await request.GetResponseAsync())
            {
                using (var sr = new StreamReader(response.GetResponseStream() ?? Stream.Null, Encoding.UTF8))
                {
                    var responseContent = await sr.ReadToEndAsync();
                    return responseContent;
                }
            }
        }

        public async Task<string> PostByTextAsync(string url, string parameters, object URLParameters = null)
        {
            byte[] byteArray = Encoding.UTF8.GetBytes(parameters); //转化

            // 根据 URL参数 拼接 URL
            if (URLParameters != null)
            {
                Type type = URLParameters.GetType();
                PropertyInfo[] piArray = type.GetProperties();
                url += "?";
                for (int i = 0; i < piArray.Length; i++)
                {
                    if ((i + 1) != piArray.Length)
                        url += $"{piArray[i].Name}={piArray[i].GetValue(URLParameters)}&";
                    else
                        url += $"{piArray[i].Name}={JsonConvert.SerializeObject(piArray[i].GetValue(URLParameters), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore })}";
                }
            }

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "POST";
            request.ContentType = "text";
            request.Proxy = null;
            request.ContentLength = byteArray.Length;
            using (Stream reqStream = request.GetRequestStream())
            {
                reqStream.Write(byteArray, 0, byteArray.Length);//写入参数
            }
            using (var response = await request.GetResponseAsync())
            {
                using (var sr = new StreamReader(response.GetResponseStream() ?? Stream.Null, Encoding.UTF8))
                {
                    var responseContent = await sr.ReadToEndAsync();
                    return responseContent;
                }
            }
        }

        public async Task<string> HttpPostAsync(string url, NameValueCollection data, string charset = "utf-8")
        {
            var parastr = string.Empty;
            var responseContent = string.Empty;
            try
            {
                StringBuilder buffer = new StringBuilder();
                int i = 0;
                foreach (string key in data.Keys)
                {
                    if (i > 0)
                    {
                        buffer.AppendFormat("&{0}={1}", key, data[key]);
                    }
                    else
                    {
                        buffer.AppendFormat("{0}={1}", key, data[key]);
                    }
                    i++;
                }
                parastr = buffer.ToString();
                byte[] byteArray = Encoding.UTF8.GetBytes(parastr); //转化
                _logger.LogInformation($"{url}-Post-Begin,param:{parastr}");

                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.Method = "POST";
                request.Proxy = null;
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = byteArray.Length;
                //request.Proxy = null;
                //request.ServicePoint.ConnectionLimit = int.MaxValue;
                //request.KeepAlive = false;
                using (Stream reqStream = request.GetRequestStream())
                {
                    reqStream.Write(byteArray, 0, byteArray.Length);//写入参数
                }
                using (var response = await request.GetResponseAsync())
                {
                    using (var sr = new StreamReader(response.GetResponseStream() ?? Stream.Null, Encoding.UTF8))
                    {
                        responseContent = await sr.ReadToEndAsync();
                        _logger.LogInformation($"{url}-Post-End,param:{parastr},result:{responseContent}");
                        return responseContent;
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"{url}-Post-End,parameters:{parastr},result:{responseContent},ErrorMsg:{ex.Message}", ex);
                throw ex;
            }
        }

        public string HttpPost(string url, NameValueCollection data, string charset = "utf-8")
        {
            var parastr = string.Empty;
            var responseContent = string.Empty;
            try
            {
                StringBuilder buffer = new StringBuilder();
                int i = 0;
                foreach (string key in data.Keys)
                {
                    if (i > 0)
                    {
                        buffer.AppendFormat("&{0}={1}", key, data[key]);
                    }
                    else
                    {
                        buffer.AppendFormat("{0}={1}", key, data[key]);
                    }
                    i++;
                }
                parastr = buffer.ToString();
                byte[] byteArray = Encoding.UTF8.GetBytes(parastr); //转化
                _logger.LogInformation($"{url}-Post-Begin,param:{parastr}");

                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.Method = "POST";
                request.Proxy = null;
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = byteArray.Length;
                //request.Proxy = null;
                //request.ServicePoint.ConnectionLimit = int.MaxValue;
                //request.KeepAlive = false;
                using (Stream reqStream = request.GetRequestStream())
                {
                    reqStream.Write(byteArray, 0, byteArray.Length);//写入参数
                }
                using (var response = request.GetResponse())
                {
                    using (var sr = new StreamReader(response.GetResponseStream() ?? Stream.Null, Encoding.UTF8))
                    {
                        responseContent = sr.ReadToEnd();
                        _logger.LogInformation($"{url}-Post-End,param:{parastr},result:{responseContent}");
                        return responseContent;
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"{url}-Post-End,parameters:{parastr},result:{responseContent},ErrorMsg:{ex.Message}", ex);
                throw ex;
            }
        }

        public async Task<string> PostFormFileAsync(string url, Dictionary<string, object> parameters, Dictionary<string, string> headers = null)
        {
            string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
            byte[] boundaryBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.ContentType = "multipart/form-data; boundary=" + boundary;
            request.Method = "POST";
            request.Proxy = null;
            request.KeepAlive = true;
            request.Credentials = System.Net.CredentialCache.DefaultCredentials;

            if (parameters != null && parameters.Count > 0)
            {
                using (Stream requestStream = await request.GetRequestStreamAsync())
                {
                    foreach (KeyValuePair<string, object> pair in parameters)
                    {
                        requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
                        if (pair.Value is FormFileBody)
                        {
                            FormFileBody file = pair.Value as FormFileBody;
                            string header = "Content-Disposition: form-data; name=\"" + pair.Key + "\"; filename=\"" + file.Name + "\"\r\nContent-Type: " + file.ContentType + "\r\n\r\n";
                            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(header);
                            await requestStream.WriteAsync(bytes, 0, bytes.Length);
                            byte[] buffer = new byte[32768];
                            int bytesRead;
                            // upload from given stream
                            while ((bytesRead = await file.Stream.ReadAsync(buffer, 0, buffer.Length)) != 0)
                                await requestStream.WriteAsync(buffer, 0, bytesRead);

                        }
                        else
                        {
                            string data = "Content-Disposition: form-data; name=\"" + pair.Key + "\"\r\n\r\n" + pair.Value;
                            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
                            await requestStream.WriteAsync(bytes, 0, bytes.Length);
                        }
                    }

                    byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
                    await requestStream.WriteAsync(trailer, 0, trailer.Length);
                    requestStream.Close();
                }
            }
            SetHeaders(request, headers);
            using (WebResponse response = await request.GetResponseAsync())
            {
                using (Stream responseStream = response.GetResponseStream())
                using (StreamReader reader = new StreamReader(responseStream))
                    return await reader.ReadToEndAsync();
            }
        }
        /// <summary>
        /// 从url读取内容到内存MemoryStream流中
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public async Task<MemoryStream> DownLoadFielToMemoryStream(string url)
        {
            var wreq = HttpWebRequest.Create(url) as HttpWebRequest;
            HttpWebResponse response = (await wreq.GetResponseAsync()) as HttpWebResponse;
            MemoryStream ms = null;
            using (var stream = response.GetResponseStream())
            {
                Byte[] buffer = new Byte[response.ContentLength];
                int offset = 0, actuallyRead = 0;
                do
                {
                    actuallyRead = await stream.ReadAsync(buffer, offset, buffer.Length - offset);
                    offset += actuallyRead;
                }
                while (actuallyRead > 0);
                ms = new MemoryStream(buffer);
            }
            response.Close();
            return ms;
        }

        private void SetHeaders(HttpWebRequest request, Dictionary<string, string> headers)
        {
            if (headers == null || !headers.Any())
            {
                return;
            }
            foreach (var kvp in headers)
            {
                if (DefaultHeaders.Contains(kvp.Key))
                {
                    continue;
                }
                request.Headers.Add(kvp.Key, kvp.Value);
            };
        }

        //public async Task<string> DeleteAsync(string url, string parameters, Dictionary<string, string> headers = null)
        //{
        //    byte[] byteArray = Encoding.UTF8.GetBytes(parameters); //转化
        //    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        //    System.Net.ServicePointManager.DefaultConnectionLimit = 100;
        //    request.Proxy = null;
        //    request.Method = "DELETE";

        //    request.ContentType = "application/json";
        //    request.ContentLength = byteArray.Length;
        //    SetHeaders(request, headers);
        //    using (Stream reqStream = request.GetRequestStream())
        //    {
        //        reqStream.Write(byteArray, 0, byteArray.Length);//写入参数
        //    }
        //    using (var response = await request.GetResponseAsync())
        //    {
        //        using (var sr = new StreamReader(response.GetResponseStream() ?? Stream.Null, Encoding.UTF8))
        //        {
        //            var responseContent = await sr.ReadToEndAsync();
        //            return responseContent;
        //        }
        //    }
        //}

        //public async Task<string> PutAsync(string url, string parameters, Dictionary<string, string> headers = null)
        //{
        //    byte[] byteArray = Encoding.UTF8.GetBytes(parameters); //转化
        //    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        //    System.Net.ServicePointManager.DefaultConnectionLimit = 100;
        //    request.Proxy = null;
        //    request.Method = "PUT";

        //    request.ContentType = "application/json";
        //    request.ContentLength = byteArray.Length;
        //    SetHeaders(request, headers);
        //    using (Stream reqStream = request.GetRequestStream())
        //    {
        //        reqStream.Write(byteArray, 0, byteArray.Length);//写入参数
        //    }
        //    using (var response = await request.GetResponseAsync())
        //    {
        //        using (var sr = new StreamReader(response.GetResponseStream() ?? Stream.Null, Encoding.UTF8))
        //        {
        //            var responseContent = await sr.ReadToEndAsync();
        //            return responseContent;
        //        }
        //    }
        //}
    }

    /// <summary>
    /// form表单文件格式
    /// </summary>
    public class FormFileBody
    {
        public string Name { get; set; }

        public string ContentType { get; set; }


        public Stream Stream { get; set; }
    }
}
